#include <rtthread.h>
#include <rtdevice.h>
#include "littlevgl2rtt.h"
#include "lvgl.h"
#include "gui_app_fwk.h"

#include "app_clock_main.h"
#include "app_mem.h"

// 是否启用时钟指针图像缓存
#define CACHE_CLOCK_HANDS 1

/**
 * 表盘时钟结构体
 * 用于存储表盘时钟的各种组件和状态信息
 */
typedef struct
{
    lv_obj_t *bg_head;               // 背景图像头部
    lv_obj_t *bg_body;               //背景图像身体
    lv_obj_t *bg;                    // 背景图像对象
    lv_obj_t *second_hand;           // 秒针图像对象
    lv_obj_t *minute_hand;           // 分针图像对象
    lv_obj_t *hour_hand;             // 时针图像对象
    lv_timer_t *redraw_task;         // 重绘定时器
    app_clock_time_t last_redraw_time; // 上次重绘时间记录

#if CACHE_CLOCK_HANDS
    lv_img_dsc_t *sec_cache;         // 秒针图像缓存
    lv_img_dsc_t *min_cache;         // 分针图像缓存
    lv_img_dsc_t *hor_cache;         // 时针图像缓存
#endif
} app_clock_dial_t;

// 声明表盘时钟所需的图像资源
LV_IMG_DECLARE(clock_mickey_bg);         // 表盘背景图像
//LV_IMG_DECLARE(clock_dial_point_s);  // 秒针图像
LV_IMG_DECLARE(clock_mickey_hand_minute);  // 分针图像
LV_IMG_DECLARE(clock_mickey_hand_hour);  // 时针图像
LV_IMG_DECLARE(clock_mickey_body);       // 身体图像
LV_IMG_DECLARE(clock_mickey_head);       // 头部图像
// 全局表盘时钟实例指针
static app_clock_dial_t *p_clk_dial = NULL;

/**
 * 表盘时钟重绘函数
 * 定时更新指针角度，刷新时钟显示，处理相关图像效果，记录时间状态
 * @param task 定时器任务参数
 */
static void app_clock_dial_redraw(lv_timer_t *task)
{
    app_clock_time_t current_time;      // 当前时间结构体
    rt_uint8_t hours, minutes, seconds; // 时、分、秒
    rt_uint16_t second_angle, minute_angle, hour_angle; //milli_seconds; // 各指针角度和毫秒数

    app_clock_main_get_current_time(&current_time); // 获取当前时间
    hours = current_time.h;
    minutes = current_time.m;
    seconds = current_time.s;
    //milli_seconds = current_time.ms;

    // 计算各指针的角度值（放大10倍以提高精度）
    // 时针：每小时30度，每分钟0.5度
    hour_angle = ((hours * 300) + (minutes * 5) + 10);
    // 分针：每分钟6度，每秒0.1度
    minute_angle = ((minutes * 60) + (seconds) + 10);
    // 秒针：每秒6度，每毫秒0.006度
    //second_angle = (seconds * 60) + (milli_seconds * 360 * 10 / 60000) + 10;

    // 实时刷新指针的位置（设置旋转角度）
    //lv_img_set_angle(p_clk_dial->second_hand, second_angle);
    lv_img_set_angle(p_clk_dial->minute_hand, minute_angle);
    lv_img_set_angle(p_clk_dial->hour_hand, hour_angle);
    
    // 记录最后一次重绘时间
    memcpy(&p_clk_dial->last_redraw_time, &current_time, sizeof(app_clock_time_t));
}

/**
 * 初始化时钟指针图像的缓存
 * 将图像数据从Flash复制到RAM中以提高渲染性能
 */
static void init_clock_hands_img(void)
{
#if CACHE_CLOCK_HANDS
    // 检查时针图像缓存是否已经分配
    if (NULL == p_clk_dial->hor_cache)
    {
        // 为时针图像分配缓存空间并复制图像数据
        p_clk_dial->hor_cache = app_cache_copy_alloc(LV_EXT_IMG_GET(clock_mickey_hand_hour), ROTATE_MEM);
        RT_ASSERT(p_clk_dial->hor_cache != NULL); // 确保分配成功
        lv_img_set_src(p_clk_dial->hour_hand, p_clk_dial->hor_cache); // 设置图像源为缓存
    }

    // 检查分针图像缓存是否已经分配
    if (NULL == p_clk_dial->min_cache)
    {
        // 为分针图像分配缓存空间并复制图像数据
        p_clk_dial->min_cache = app_cache_copy_alloc(LV_EXT_IMG_GET(clock_mickey_hand_minute), ROTATE_MEM);
        RT_ASSERT(p_clk_dial->min_cache != NULL); // 确保分配成功
        lv_img_set_src(p_clk_dial->minute_hand, p_clk_dial->min_cache); // 设置图像源为缓存
    }

#if 0    // 检查秒针图像缓存是否已经分配
    if (NULL == p_clk_dial->sec_cache)
    {
        // 为秒针图像分配缓存空间并复制图像数据
        p_clk_dial->sec_cache = app_cache_copy_alloc(LV_EXT_IMG_GET(clock_dial_point_s), ROTATE_MEM);
        RT_ASSERT(p_clk_dial->sec_cache != NULL); // 确保分配成功
        lv_img_set_src(p_clk_dial->second_hand, p_clk_dial->sec_cache); // 设置图像源为缓存
    }
#endif
#endif

    // 设置各指针的旋转中心点（轴心）
    lv_img_set_pivot(p_clk_dial->hour_hand, 162, 36);   // 设置时针旋转中心
    lv_img_set_pivot(p_clk_dial->minute_hand, 30, 183); // 设置分针旋转中心
    //lv_img_set_pivot(p_clk_dial->second_hand, 8, 160);  // 设置秒针旋转中心

    // 对齐各指针到表盘中心的相对位置
    lv_obj_align(p_clk_dial->hour_hand, LV_ALIGN_CENTER, 0, 0);   // 时针位置
    lv_obj_align(p_clk_dial->minute_hand, LV_ALIGN_CENTER, -10, 10); // 分针位置
    //lv_obj_align(p_clk_dial->second_hand, LV_ALIGN_CENTER, 0, -70); // 秒针位置
}

/**
 * 释放指针图像的缓存资源
 * 完成缓存功能的清理工作，避免内存泄漏
 */
static void deinit_clock_hands_img(void)
{
#if CACHE_CLOCK_HANDS
    // 将时钟的图像恢复为原始定义（存储在Flash中的图像）
    lv_img_set_src(p_clk_dial->hour_hand, LV_EXT_IMG_GET(clock_mickey_hand_hour));
    lv_img_set_src(p_clk_dial->minute_hand, LV_EXT_IMG_GET(clock_mickey_hand_minute));
    //lv_img_set_src(p_clk_dial->second_hand, LV_EXT_IMG_GET(clock_dial_point_s));
    
    // 重置指针角度为0，因为Flash中的图像不能旋转
    lv_img_set_angle(p_clk_dial->hour_hand, 0);
    lv_img_set_angle(p_clk_dial->minute_hand, 0);
    //lv_img_set_angle(p_clk_dial->second_hand, 0);

    // 释放时针图像缓存，避免内存泄漏
    if (p_clk_dial->hor_cache != NULL)
    {
        app_cache_copy_free(p_clk_dial->hor_cache);
        p_clk_dial->hor_cache = NULL;
    }

    // 释放分针图像缓存，避免内存泄漏
    if (p_clk_dial->min_cache != NULL)
    {
        app_cache_copy_free(p_clk_dial->min_cache);
        p_clk_dial->min_cache = NULL;
    }

#if 0    // 释放秒针图像缓存，避免内存泄漏
    if (p_clk_dial->sec_cache != NULL)
    {
        app_cache_copy_free(p_clk_dial->sec_cache);
        p_clk_dial->sec_cache = NULL;
    }
#endif
#endif
}

/**
 * 恢复回调函数
 * 当表盘时钟从暂停状态恢复时调用
 * @return RT_EOK 操作成功
 */
static rt_int32_t resume_callback(void)
{
    init_clock_hands_img(); // 初始化时钟指针图像缓存

    // 如果重绘定时器未创建，则创建一个30ms间隔的定时器
    if (NULL == p_clk_dial->redraw_task)
    {
        p_clk_dial->redraw_task = lv_timer_create(app_clock_dial_redraw, 30, (void *)0);
    }

    return RT_EOK;
}

/**
 * 暂停回调函数
 * 当表盘时钟进入暂停状态时调用
 * @return RT_EOK 操作成功
 */
static rt_int32_t pause_callback(void)
{
    // 删除重绘定时器
    lv_timer_del(p_clk_dial->redraw_task);
    p_clk_dial->redraw_task = NULL;

    deinit_clock_hands_img(); // 释放图像缓存资源

    return RT_EOK;
}

/**
 * 图像缩放设置函数
 * 根据传入目标尺寸，对指定的图像对象进行缩放设置
 * @param obj_img 图像对象
 * @param src 图像源
 * @param obj_width 目标宽度
 * @param obj_height 目标高度
 */
static void lv_obj_img_png_set_zoom(lv_obj_t *obj_img, const char *src, uint32_t obj_width, uint32_t obj_height)
{
    uint32_t img_width = 0, img_height = 0, zoom_factor = 0;
    lv_img_header_t header;
    
    // 获取传入图像源的信息
    if (lv_img_decoder_get_info(src, &header) != LV_RES_OK)
    {
        rt_kprintf("lv_img_decoder_get_info error\n");
        return;
    }

    img_width = header.w;   // 原始图像宽度
    img_height = header.h;  // 原始图像高度

    rt_kprintf("img_width:%u, img_height%u, obj_width:%u, obj_height%u\n", img_width, img_height, obj_width, obj_height);
    
    // 确保图像尺寸有效
    if (img_width != 0 && img_height != 0)
    {
        uint32_t y_a = obj_height;
        uint32_t x_b = obj_width;

        // 根据目标尺寸计算缩放因子
        if (x_b <= y_a)
        {
            // 按高度比例缩放
            uint32_t x = obj_height * 256;
            zoom_factor = x / img_height;
            lv_img_set_zoom(obj_img, zoom_factor);
        }
        else
        {
            // 按宽度比例缩放
            uint32_t x = obj_width * 256;
            zoom_factor = x / img_width;
            lv_img_set_zoom(obj_img, zoom_factor);
        }
    }
}

/**
 * 初始化函数
 * 创建表盘时钟的所有GUI组件
 * @param parent 父对象
 * @return RT_EOK 操作成功
 */
static rt_int32_t init(lv_obj_t *parent)
{
    // 分配并初始化表盘时钟结构体内存
    p_clk_dial = (app_clock_dial_t *) rt_malloc(sizeof(app_clock_dial_t));
    memset(p_clk_dial, 0, sizeof(app_clock_dial_t));

    // 创建一个容器作为父对象
    lv_obj_t *my_parent = lv_obj_create(parent);
    lv_obj_set_size(my_parent, lv_obj_get_width(parent), lv_obj_get_height(parent)); // 设置容器大小
    lv_obj_clear_flag(my_parent, LV_OBJ_FLAG_SCROLLABLE); // 禁用滚动功能

    // 创建背景图像
    p_clk_dial->bg = lv_img_create(my_parent);
    p_clk_dial->bg_body = lv_img_create(p_clk_dial->bg);
    p_clk_dial->bg_head = lv_img_create(p_clk_dial->bg);
    lv_img_set_src(p_clk_dial->bg, LV_EXT_IMG_GET(clock_mickey_bg)); // 设置背景图像源
    lv_img_set_src(p_clk_dial->bg_body, LV_EXT_IMG_GET(clock_mickey_body)); // 设置身体
    lv_img_set_src(p_clk_dial->bg_head, LV_EXT_IMG_GET(clock_mickey_head)); // 添加头
    lv_obj_align(p_clk_dial->bg, LV_ALIGN_CENTER, 0, 0); // 居中对齐
    lv_obj_align(p_clk_dial->bg_body, LV_ALIGN_CENTER, 0, 80); 
    lv_obj_align(p_clk_dial->bg_head, LV_ALIGN_CENTER, 0, -80); 
    // 根据父对象尺寸缩放背景图像
    lv_obj_img_png_set_zoom(p_clk_dial->bg, LV_EXT_IMG_GET(clock_mickey_bg), lv_obj_get_width(parent), lv_obj_get_height(parent));
    lv_obj_img_png_set_zoom(p_clk_dial->bg_body, LV_EXT_IMG_GET(clock_mickey_body), lv_obj_get_width(parent)/2.5, lv_obj_get_height(parent)/2.5);
    lv_obj_img_png_set_zoom(p_clk_dial->bg_head, LV_EXT_IMG_GET(clock_mickey_head), lv_obj_get_width(parent)/2.5, lv_obj_get_height(parent)/2.5);
    // 创建时钟指针图像对象
    p_clk_dial->hour_hand = lv_img_create(p_clk_dial->bg);     // 时针
    p_clk_dial->minute_hand = lv_img_create(p_clk_dial->bg);   // 分针
    //p_clk_dial->second_hand = lv_img_create(p_clk_dial->bg);   // 秒针

    // 设置指针图像源
    lv_img_set_src(p_clk_dial->hour_hand, LV_EXT_IMG_GET(clock_mickey_hand_hour));
    lv_img_set_src(p_clk_dial->minute_hand, LV_EXT_IMG_GET(clock_mickey_hand_minute));
    //lv_img_set_src(p_clk_dial->second_hand, LV_EXT_IMG_GET(clock_dial_point_s));

    // 初始化重绘定时器为空
    p_clk_dial->redraw_task = NULL;

    return RT_EOK;
}

/**
 * 反初始化函数
 * 释放表盘时钟占用的所有资源
 * @return RT_EOK 操作成功
 */
static rt_int32_t deinit(void)
{
    if (p_clk_dial)
    {
        // 如果重绘定时器存在，则删除它
        if (p_clk_dial->redraw_task)
        {
            lv_timer_del(p_clk_dial->redraw_task);
        }
        // 释放结构体内存
        rt_free(p_clk_dial);
        p_clk_dial = NULL;
    }

    return RT_EOK;
}

// 定义表盘时钟的操作函数表
static const app_clock_ops_t ops =
{
    .init = init,        // 初始化函数
    .pause = pause_callback,   // 暂停函数
    .resume = resume_callback, // 恢复函数
    .deinit = deinit,    // 反初始化函数
};

/**
 * 注册表盘时钟应用
 * 将表盘时钟注册到时钟管理系统中
 */
void app_clock_dial_register(void)
{
    app_clock_register("dial", &ops); // 注册表盘时钟，ID为"dial"
}